package org.pwwong.fxscalautils.components.containers

import com.sun.scenario.scenegraph.fx.FXGroup

import org.pwwong.scalautils.signals.Predef._
import org.pwwong.scalautils.signals.{Signal,ConstantSignal,BindableSignal}
import org.pwwong.scalautils.time.Predef._

import scala.collection.jcl
import scala.collection.mutable

/**
 * LayoutManager manages when and which Containers need to perform a relayout.
 * LayoutManager group requests from all containers, remove duplicate requests from the same container, and 
 * kick-off relayouts in an organized manner and regular time (60 fps).
 * The LayoutManager ultimately improves performance by reducing the number relayouts that happen.
 * 
 * Containers make requests to relayout, and the LayoutManager grants that request at the appropriate time.
 * Containers make relayout requests because atleast one of the following changes:
 *   1) size (curWidth, curHeight)
 *   2) child's size (w,h)
 *   3) child's position (x,y)
 *   4) child's preferred size (preferredWidth, preferredHeight)
 *   5) child's anchor (anchorx, anchory)
 * 
 * This may seem backwards for Containers to request relayout from the LayoutManager, but only the Container
 * knows whether or not it needs a relayout.   
 */
object LayoutManager {
  // Map holding containers requesting relayout
  private val _dirtyContainerMap = new jcl.TreeMap[Int,mutable.Queue[Container]]()
  
  // Map holding containers requesting relayout
  private var _layoutTimedSignal:Signal[Unit] = null 
  private def doPeriodicLayout {
    def layoutDirtyLevel(q:mutable.Queue[Container]):Unit = {
      if(!q.isEmpty) {
        q.dequeue.relayout  // Make this a parrellel process, all relayouts on the same level can occur at the same time 
        layoutDirtyLevel(q)
      }
    }
    for( lcs <- _dirtyContainerMap) layoutDirtyLevel(lcs._2)
  }
  
  var relayoutCount = 0
  
  /**
   * Starts up the LayoutManager.  This is called by the NewFangleApp Class on startup.
   */
  def start {
    // Start Counter
    eval { println("Relayouts Per Second: "+relayoutCount); relayoutCount = 0 } every(1 secs) toSignal
    
    if(_layoutTimedSignal==null) _layoutTimedSignal = eval { doPeriodicLayout } every(16 ms)  
  }
  
  /**
   * Requests the specified Container relayout.  This requests will be handled at the next 60fps iteration.
   */
  protected[containers] def requestRelayout(c:Container) {
    if(c.containerLevel() > -1){
      if(!_dirtyContainerMap.contains(c.containerLevel())) _dirtyContainerMap += (c.containerLevel() -> new mutable.Queue[Container]())
      //println("request("+c.containerLevel()+", "+c.getClass.getName+")")
      _dirtyContainerMap(c.containerLevel()) += c
    }
  }
}